<h1>模板处理</h1>

<p>beego 的模板处理引擎采用的是 Go 内置的 <code>html/template</code> 包进行处理，而且 beego 的模板处理逻辑是采用了缓存编译方式，也就是所有的模板会在 beego 应用启动的时候全部编译然后缓存在 map 里面。</p>

<h2>模板目录</h2>

<p>beego 中默认的模板目录是 <code>views</code>，用户可以把模板文件放到该目录下，beego 会自动在该目录下的所有模板文件进行解析并缓存，开发模式下每次都会重新解析，不做缓存。当然，用户也可以通过如下的方式改变模板的目录（只能指定一个目录为模板目录）：</p>

<pre><code>beego.ViewsPath = &quot;myviewpath&quot;
</code></pre>

<h2>自动渲染</h2>

<p>用户无需手动的调用渲染输出模板，beego 会自动的在调用完相应的 method 方法之后调用 Render 函数，当然如果您的应用是不需要模板输出的，那么可以在配置文件或者在 <code>main.go</code> 中设置关闭自动渲染。</p>

<p>配置文件配置如下：</p>

<pre><code>autorender = false
</code></pre>

<p>main.go 文件中设置如下：</p>

<pre><code>beego.AutoRender = false
</code></pre>

<h2>模板标签</h2>

<p>Go 语言的默认模板采用了 <code>{ {</code> 和 <code>} }</code> 作为左右标签，但是我们有时候在开发中可能界面是采用了 AngularJS 开发，他的模板也是这个标签，故而引起了冲突。在 beego 中你可以通过配置文件或者直接设置配置变量修改：</p>

<pre><code>beego.TemplateLeft = &quot;&lt;&lt;&lt;&quot;
beego.TemplateRight = &quot;&gt;&gt;&gt;&quot;
</code></pre>

<h2>模板数据</h2>

<p>模板中的数据是通过在 Controller 中 <code>this.Data</code> 获取的，所以如果你想在模板中获取内容 <code>{ {.Content} }</code> ,那么你需要在 Controller 中如下设置：</p>

<pre><code>this.Data[&quot;Content&quot;] = &quot;value&quot;
</code></pre>

<p>如何使用各种类型的数据渲染：</p>

<ul>
    <li><p>结构体</p>

        <p>结构体结构</p>

        <pre><code>type A struct{
    Name string
    Age  int
}
</code></pre>

        <p>控制器数据赋值</p>

        <pre><code>this.Data[&quot;a&quot;]=&amp;A{Name:&quot;astaxie&quot;,Age:25}
</code></pre>

        <p>模板渲染数据如下：</p>

        <pre><code>the username is { {.a.Name} }
                the age is { {.a.Age} }
</code></pre></li>

    <li><p>map</p>

        <p>控制器数据赋值</p>

        <pre><code>mp[&quot;name&quot;]=&quot;astaxie&quot;
mp[&quot;nickname&quot;] = &quot;haha&quot;
this.Data[&quot;m&quot;]=mp
</code></pre>

        <p>模板渲染数据如下：</p>

        <pre><code>the username is { {.m.name} }
                the username is { {.m.nickname} }
</code></pre></li>

    <li><p>slice</p>

        <p>控制器数据赋值</p>

        <pre><code>ss :=[]string{&quot;a&quot;,&quot;b&quot;,&quot;c&quot;}
this.Data[&quot;s&quot;]=ss
</code></pre>

        <p>模板渲染数据如下：</p>

        <pre><code>{ {range $key, $val := .s} }
{ {$key} }
{ {$val} }
{ {end} }
</code></pre></li>
</ul>

<h2>模板名称</h2>

<p>beego 采用了 Go 语言内置的模板引擎，所有模板的语法和 Go 的一模一样，至于如何写模板文件，详细的请参考 <a href="https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/07.4.md">模板教程</a>。</p>

<p>用户通过在 Controller 的对应方法中设置相应的模板名称，beego 会自动的在 viewpath 目录下查询该文件并渲染，例如下面的设置，beego 会在 admin 下面找 add.tpl 文件进行渲染：</p>

<pre><code>this.TplName = &quot;admin/add.tpl&quot;
</code></pre>

<p>我们看到上面的模板后缀名是 tpl，beego 默认情况下支持 tpl 和 html 后缀名的模板文件，如果你的后缀名不是这两种，请进行如下设置：</p>

<pre><code>beego.AddTemplateExt(&quot;你文件的后缀名&quot;)
</code></pre>

<p>当你设置了自动渲染，然后在你的 Controller 中没有设置任何的 TplName，那么 beego 会自动设置你的模板文件如下：</p>

<pre><code>c.TplName = strings.ToLower(c.controllerName) + &quot;/&quot; + strings.ToLower(c.actionName) + &quot;.&quot; + c.TplExt
</code></pre>

<p>也就是你对应的 Controller 名字+请求方法名.模板后缀，也就是如果你的 Controller 名是 <code>AddController</code>，请求方法是 <code>POST</code>，默认的文件后缀是 <code>tpl</code>，那么就会默认请求 <code>/viewpath/AddController/post.tpl</code> 文件。</p>

<h2>Layout 设计</h2>

<p>beego 支持 layout 设计，例如你在管理系统中，整个管理界面是固定的，只会变化中间的部分，那么你可以通过如下的设置：</p>

<pre><code>this.Layout = &quot;admin/layout.html&quot;
this.TplName = &quot;admin/add.tpl&quot;
</code></pre>

<p>在 layout.html 中你必须设置如下的变量：</p>

<pre><code>{ {.LayoutContent} }
</code></pre>

<p>beego 就会首先解析 TplName 指定的文件，获取内容赋值给 LayoutContent，然后最后渲染 layout.html 文件。</p>

<p>目前采用首先把目录下所有的文件进行缓存，所以用户还可以通过类似这样的方式实现 layout：</p>

<pre><code>{ {template &quot;header.html&quot; .} }
        Logic code
        { {template &quot;footer.html&quot; .} }
</code></pre>

<blockquote>
    <blockquote>
        <blockquote>
            <p>特别注意后面的<code>.</code>,这是传递当前参数到子模板</p>
        </blockquote>
    </blockquote>
</blockquote>

<h2>LayoutSection</h2>

<p>对于一个复杂的 <code>LayoutContent</code>，其中可能包括有javascript脚本、CSS 引用等，根据惯例，通常 css 会放到 Head 元素中，javascript 脚本需要放到 body 元素的末尾，而其它内容则根据需要放在合适的位置。在 <code>Layout</code> 页中仅有一个 <code>LayoutContent</code> 是不够的。所以在 <code>Controller</code> 中增加了一个 <code>LayoutSections</code>属性，可以允许 <code>Layout</code> 页中设置多个 <code>section</code>，然后每个 <code>section</code> 可以分别包含各自的子模板页。</p>

<p>layout_blog.tpl:</p>

<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Lin Li&lt;/title&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css&quot;&gt;
    { {.HtmlHead} }
        &lt;/head&gt;
        &lt;body&gt;

        &lt;div class=&quot;container&quot;&gt;
        { {.LayoutContent} }
        &lt;/div&gt;
        &lt;div&gt;
        { {.SideBar} }
        &lt;/div&gt;
        &lt;script type=&quot;text/javascript&quot; src=&quot;http://code.jquery.com/jquery-2.0.3.min.js&quot;&gt;&lt;/script&gt;
        &lt;script src=&quot;http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js&quot;&gt;&lt;/script&gt;
        { {.Scripts} }
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>html_head.tpl:</p>

<pre><code>&lt;style&gt;
     h1 {
        color: red;
     }
&lt;/style&gt;
</code></pre>

<p>scripts.tpl：</p>

<pre><code>&lt;script type=&quot;text/javascript&quot;&gt;
    $(document).ready(function() {
        // bla bla bla
    });
&lt;/script&gt;
</code></pre>

<p>逻辑处理如下所示：</p>

<pre><code>type BlogsController struct {
    beego.Controller
}

func (this *BlogsController) Get() {
    this.Layout = &quot;layout_blog.tpl&quot;
    this.TplName = &quot;blogs/index.tpl&quot;
    this.LayoutSections = make(map[string]string)
    this.LayoutSections[&quot;HtmlHead&quot;] = &quot;blogs/html_head.tpl&quot;
    this.LayoutSections[&quot;Scripts&quot;] = &quot;blogs/scripts.tpl&quot;
    this.LayoutSections[&quot;Sidebar&quot;] = &quot;&quot;
}
</code></pre>

<h2>renderform 使用</h2>

<p>定义 struct:</p>

<pre><code>type User struct {
    Id    int         `form:&quot;-&quot;`
    Name  interface{} `form:&quot;username&quot;`
    Age   int         `form:&quot;age,text,年龄：&quot;`
    Sex   string
    Intro string `form:&quot;,textarea&quot;`
}
</code></pre>

<ul>
    <li>StructTag 的定义用的标签用为 <code>form</code>，和 <a href="../controller/params.md#%E7%9B%B4%E6%8E%A5%E8%A7%A3%E6%9E%90%E5%88%B0-struct">ParseForm 方法</a> 共用一个标签，标签后面有三个可选参数，用 <code>,</code> 分割。第一个参数为表单中类型的 <code>name</code> 的值，如果为空，则以 <code>struct field name</code> 为值。第二个参数为表单组件的类型，如果为空，则为 <code>text</code>。表单组件的标签默认为 <code>struct field name</code> 的值，否则为第三个值。<br /></li>
    <li>如果 <code>form</code> 标签只有一个值，则为表单中类型 <code>name</code> 的值，除了最后一个值可以忽略外，其他位置的必须要有 <code>,</code> 号分割，如：<code>form:&quot;,,姓名：&quot;</code><br /></li>
    <li>如果要忽略一个字段，有两种办法，一是：字段名小写开头，二是：<code>form</code> 标签的值设置为 <code>-</code><br /></li>
    <li>现在的代码版本只能实现固定的格式，用 br 标签实现换行，无法实现 css 和 class 等代码的插入。所以，要实现 form 的高级排版，不能使用 renderform 的方法，而需要手动处理每一个字段。<br /></li>
</ul>

<p>controller：</p>

<pre><code>func (this *AddController) Get() {
    this.Data[&quot;Form&quot;] = &amp;User{}
    this.TplName = &quot;index.tpl&quot;
}
</code></pre>

<p>Form 的参数必须是一个 struct 的指针。</p>

<p>template:</p>

<pre><code>&lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
{ {.Form | renderform} }
&lt;/form&gt;
</code></pre>

<p>上面的代码生成的表单为：</p>

<pre><code>    Name: &lt;input name=&quot;username&quot; type=&quot;text&quot; value=&quot;test&quot;&gt;&lt;/br&gt;
    年龄：&lt;input name=&quot;age&quot; type=&quot;text&quot; value=&quot;0&quot;&gt;&lt;/br&gt;
    Sex: &lt;input name=&quot;Sex&quot; type=&quot;text&quot; value=&quot;&quot;&gt;&lt;/br&gt;
    Intro: &lt;input name=&quot;Intro&quot; type=&quot;textarea&quot; value=&quot;&quot;&gt;
</code></pre>

</body>
